home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-11-30 | 19.3 KB | 811 lines | [TEXT/KAHL] |
- /*************************************************************************
- Apple Macintosh Developer Technical Support
-
- Some of Forrest Tanaka's Famous High Level Off-Screen Map Routines
- [ Reference: Macintosh Technical Note #120 ]
-
- "CColorBitMap.cp"
-
- These routines provide a high-level interface to the QuickDraw & Color
- Manager routines which allow the creation and manipulation of off-screen
- bitmaps and pixmaps. They are designed to run on any machine with 128K or
- later ROMs.
-
- NOTE that I've modified some of Forrest's routines for the OOP world
- and, therefore, any resultant errors belong solely to me.
-
- using Symantec's "THINK C / C++", v 6/7
- *************************************************************************/
-
-
-
-
- #include <CView.h>
- #include <Exceptions.h> /* Has declaration of "kSilentErr" */
- #include <TCLUtilities.h> /* ... and SetAllocation(). */
- #include <GestaltEqu.h>
- #include <LongQD.h>
- #include <OSChecks.h>
- #include <Traps.h>
-
- #include <Global.h>
- #include "CColorBitMap.h"
-
-
- extern short gLastError;
-
-
-
- /*
- ** Call:
- **
- ** FailOSErr( error );
- **
- ** which <IF error ≠ noErr> then calls:
- **
- ** Failure( error, message = 0 );
- **
- ** which sets gLastError = error and gLastMessage = message
- ** AND via your TRY-CATCH-ENDTRY Macros propagates the error
- ** up to the TRY-CATCH-ENDTRY Handlers within CApplication::Run().
- ** The latter's CATCH Handler will call:
- **
- ** if (gLastError != kSilentErr) // kSilentErr = 0.
- ** ErrorAlert( gLastError, gLastMessage );
- **
- ** and then RETRY what you have in your TRY Macro or Handler.
- ** Because FailOSErr passed a message = 0 to Failure, ErrorAlert
- ** will look for a 'Estr' resource with an ID = the passed error.
- */
-
- static void ErrorOut (OSErr error)
- {
- FailOSErr( error );
-
- } /* ErrorOut */
-
-
-
- /**************************
- Just plain nifty stuff ...
- **************************/
-
- void CColorBitMap::IColorBitMap (short width, short height, Boolean makePort)
- {
- Rect theBounds;
- long qdVersion, tempSeed;
- OSErr err;
- Boolean depthIndexed, depthDirect, qd32BitInstalled, savedAlloc;
- short maxDepth, offRowBytes;
- unsigned long sizeOfOff;
-
-
- /*
- NOTE: I will ignore "makePort" because my bitMap/pixMap
- is intricately tied to the off-screen Port.
- */
-
- /*
- I don't think so !!!!!
-
- MoveHHi( (Handle)this );
- HLock( (Handle)this );
- */
-
- /* Initialize some stuff ... */
-
- xferMode = srcCopy;
- macPort = nil;
- macBitMap = nil;
- itsPixMap = nil;
- macBits = nil;
- itsColors = nil;
- itsMaxDevice = nil;
-
- SetRect( &theBounds, 0, 0, width, height );
-
- /* ErrorOut( noErr ); -- We hope !! */
-
- GetPort( &savePort ); /* Also used by superclass'
- BeginDrawing & EndDrawing. */
- if (!gSystem.hasColorQD)
- {
- savedAlloc = SetAllocation( kAllocCanFail );
- macPort = (GrafPtr) NewPtrClear( sizeof(GrafPort) );
- SetAllocation( savedAlloc );
- if (macPort == nil)
- {
- ErrorOut( NewPortError );
- /*
- ** return; -- ErrorOut will automatically pop the return
- ** address off the Stack when FailOSErr is called.
- */
- }
- OpenPort( macPort );
- maxDepth = 1;
-
- } /* ... a low-life machine */
-
- else
- {
- /* Initialize this guy: */
- savedAlloc = SetAllocation( kAllocCanFail );
- macPort = (GrafPtr) NewPtrClear( sizeof(CGrafPort) );
- SetAllocation( savedAlloc );
- if (macPort == nil)
- {
- ErrorOut( NewPortError );
- }
- OpenCPort( (CGrafPtr)macPort );
- itsPixMap = ((CGrafPtr) macPort)->portPixMap;
- /*
- When we call _CopyBits within our CopyFrom
- and CopyTo methods we pass:
-
- &((GrafPtr) macPort)->portBits;
-
- which is really a Pointer to a PixMapHandle.
- _CopyBits recognizes that and does the
- appropriate de-referencing. As a result,
- this Handle can float.
-
- MoveHHi( (Handle)itsPixMap );
- HLock( (Handle)itsPixMap );
- */
-
- maxDepth = (**itsPixMap).pixelSize;
-
- if ( TrapAvailable(_Gestalt) )
- {
- Gestalt( gestaltQuickdrawVersion, & qdVersion );
- qd32BitInstalled = qdVersion >= gestalt32BitQD;
- }
- else
- qd32BitInstalled = FALSE;
-
- depthIndexed = maxDepth <= 8;
- depthDirect = (maxDepth > 8) && qd32BitInstalled;
-
- if ( !depthIndexed && !depthDirect )
- {
- SetPort( savePort );
- ErrorOut( DepthError );
- }
-
- }; /* else: hasColorQD */
-
- /*
- Before we do ANYthing more, we should set the off-screen's
- visRgn to the FULL size of the input rect so the image stays
- whole even when the window has been dragged partly beyond
- the physical edge(s) of the screen. Otherwise, the
- (**visRgn).rgnBBox in local coordinates remains equal to
- screenBits.bounds as initialized when _Open(C)Port was called:
- */
-
- RectRgn( macPort->visRgn, &theBounds );
- macPort->portRect = theBounds;
- ClipRect( &theBounds );
-
- /*
- We are now ready to calculate the size of the pixel image we will need.
- Then we can set the location-specific and size-specific information of
- the pixel map by calling SetupPixMap if we have color or stuffing
- directly if in black-and-white.
- */
-
- /* # of words: */
- offRowBytes = (maxDepth * (theBounds.right - theBounds.left) + 15) / 16;
- if (offRowBytes % 2) offRowBytes++; /* ... made even. */
- offRowBytes *= 2; /* Back to bytes. */
- if (offRowBytes > kMaxRowBytes)
- {
- SetPort( savePort );
- ErrorOut( MaxRowBytesError );
- }
-
- sizeOfOff = (theBounds.bottom - theBounds.top) * (unsigned long)offRowBytes;
-
- /* Allocate space for the pixel image: */
- savedAlloc = SetAllocation( kAllocCanFail );
- ReserveMem( sizeOfOff ); /* Around forever !!! */
- macBits = NewHandleClear( sizeOfOff );
- SetAllocation( savedAlloc );
- if (macBits == nil)
- {
- SetPort( savePort );
- ErrorOut( NewBaseAddrPtrError ); /* Bye-Bye !!! */
- }
- HLock( macBits );
-
- /*
- NOTE that we're filling in the BitMap/PixMap fields of the new Port
- directly, so we do NOT call _SetPortBits or _SetCPortPix later:
- */
-
- if (gSystem.hasColorQD)
- {
- itsColors = SetupPixMap( itsPixMap, macBits, offRowBytes, &theBounds );
- if (itsColors == nil)
- {
- SetPort( savePort );
- ErrorOut( ColorTableError );
- }
-
- itsMaxDevice = CreateGDevice( itsPixMap );
- if (itsMaxDevice == nil)
- {
- SetPort( savePort );
- ErrorOut( CreateGDeviceError );
- }
-
- /*
- We effectively pass "itsPixMap" to _CopyBits with:
-
- &((GrafPtr) macPort)->portBits;
-
- In this manner, we avoid having to HLock "itsPixMap".
-
- macBitMap = (BitMapPtr) *itsPixMap;
- */
-
- } /* if aMac2 */
-
- else /* definitely ... "YUCKY" black-and-white. */
- {
- macPort->portBits.baseAddr = *macBits;
- macPort->portBits.rowBytes = offRowBytes;
- macPort->portBits.bounds = theBounds;
-
- /* macBitMap = &macPort->portBits; -- also works in B&W. */
- };
-
- SetPort( savePort );
-
- CView::ForceNextPrepare();
-
- ErrorOut ( noErr ); /* Whew !! */
-
- } /* IColorBitMap */
-
-
-
- /****************************
- Out with the new.
-
- Whoops -- I meant the old !!
- **************************** */
-
- /* OVERRIDE: */
- void CColorBitMap::Dispose (void)
- {
-
- if (macPort != nil)
- {
- if (gSystem.hasColorQD)
- {
- if (itsColors /* ≠ nil */) DisposCTable( itsColors );
- /* HUnlock( (Handle)itsPixMap ); */
- CloseCPort( (CGrafPtr)macPort );
-
- if (itsMaxDevice)
- {
- /* OH-OH !!!!! */
- DisposHandle( (Handle) (**itsMaxDevice).gdITable );
- DisposHandle( (Handle)itsMaxDevice );
- }
- }
- else /* Yucky B&W */
- {
- ClosePort( macPort );
- }
-
- DisposPtr( (Ptr)macPort );
- macPort = nil; /* So Dispose() below doesn't try to do */
- /* it again. Otherwise, BIG time bomb !!! */
- } /* macPort ≠ nil */
-
- if (macBits)
- {
- HUnlock( macBits );
- DisposHandle( macBits );
- macBits = nil;
- }
- ;
- macBitMap = nil; /* No need to repeat yourself !! */
-
- CBitMap::Dispose(); /* Kiss him good-bye !! */
-
- } /* Dispose */
-
-
-
- void CColorBitMap::Update (void)
- {
- /* NEVER called when gSystem.hasColorQD = FALSE. */
-
- CColorBitMap *updatedBitMap = NULL;
- LongRect boundsRect;
- Rect boundsR;
- RGBColor aBlackColor = {0x0000, 0x0000, 0x0000},
- aWhiteColor = {0xFFFF, 0xFFFF, 0xFFFF};
-
- TRY
- {
- updatedBitMap = new (CColorBitMap);
- GetBounds( &boundsRect );
- updatedBitMap->IColorBitMap( (short) (boundsRect.right - boundsRect.left),
- (short) (boundsRect.bottom - boundsRect.top ),
- TRUE );
- }
- CATCH
- {
- if (updatedBitMap) updatedBitMap->Dispose();
- }
- ENDTRY;
-
- /*
- All this just 'cause I haven't YET!!! figured out
- how to update the bits covered by my Objects.
- */
-
- updatedBitMap->BeginDrawing();
- ;
- RGBForeColor( &aBlackColor );
- RGBBackColor( &aWhiteColor );
- LongToQDRect( &boundsRect, &boundsR );
- /*
- See within IColorBitMap that "macPort" = GrafPtr.
- Therefore, it need NOT be typecast:
- */
- CopyBits( &(/*(GrafPtr)*/ this->macPort)->portBits,
- &updatedBitMap->macPort->portBits,
- &boundsR, &boundsR,
- xferMode, NULL );
- ;
- updatedBitMap->EndDrawing();
-
- /*
- Then, dispose of the instance variables of THIS object
- and finally reset these variables to the updated ones:
-
- <it couldn't get much simpler than this !!!>
- */
-
- if (macPort != nil)
- {
- if (itsColors) DisposCTable( itsColors );
- /* HUnlock( (Handle)itsPixMap ); */
- CloseCPort( (CGrafPtr)macPort );
-
- if (itsMaxDevice) DisposHandle( (Handle)itsMaxDevice );
-
- DisposPtr( (Ptr)macPort );
-
- } /* macPort ≠ nil */
-
- if (macBits)
- {
- HUnlock( macBits );
- DisposHandle( macBits );
- }
-
- macPort = updatedBitMap->macPort;
- itsPixMap = updatedBitMap->itsPixMap;
- macBits = updatedBitMap->macBits;
- itsColors = updatedBitMap->itsColors;
- itsMaxDevice = updatedBitMap->itsMaxDevice;
- macBitMap = updatedBitMap->macBitMap;
-
- /*
- So far, we've transferred the pertinent variables
- from the new updated CBitMap to the old. In addition,
- we've kept the old object (= THIS); however, we still
- need to trash the new object reference:
- */
-
- delete( updatedBitMap ); /* = CObject::Dispose() */
-
- } /* Update */
-
-
-
- /* OVERRIDE: */
- Boolean CColorBitMap::PixelIsBlack (LongPt *pixelPos)
- {
- /*
- This method is overriden SOLELY because I need
- to avoid all reference to "macBitMap" in order
- to NOT have to HLock the "itsPixMap":
- */
-
- short hPos, vPos;
- long bitNum;
- LongRect bounds;
- BitMapPtr theBitMap;
-
-
- GetBounds( &bounds );
- if ( !PtInLongRect(pixelPos, &bounds) )
- return (FALSE); /* Point is not in bitmap. */
-
- if (gSystem.hasColorQD)
- theBitMap = (BitMapPtr) *itsPixMap; /* Nothing below moves memory. */
- else
- theBitMap = &thePort->portBits;
-
- hPos = pixelPos->h - theBitMap->bounds.left;
- vPos = pixelPos->v - theBitMap->bounds.top;
-
- /*
- Point is within BitMap. Convert point into
- a bit offset from the start of the image:
- */
-
- bitNum = (long)vPos * theBitMap->rowBytes * 8L + hPos;
- return ( BitTst(theBitMap->baseAddr, bitNum) );
-
- } /* PixelIsBlack */
-
-
-
- /* OVERRIDE: */
- void CColorBitMap::BeginDrawing (void)
- {
- /*
- This first "if-else" clause simply duplicates
- CBitMap's BeginDrawing with the exception that
- it addresses color QuickDraw and avoids any
- reference to "macBitMap":
- */
-
- if (macPort == NULL)
- {
- if (gSystem.hasColorQD)
- {
- savePixMap = ((CGrafPtr) thePort)->portPixMap;
- SetPortPix( itsPixMap );
- }
- else
- {
- saveBitMap = thePort->portBits;
- SetPortBits( &macPort->portBits );
- }
- }
- else
- {
- GetPort( &savePort );
- SetPort( macPort );
- }
-
- if (gSystem.hasColorQD)
- {
- saveDevice = GetGDevice();
- SetGDevice( itsMaxDevice );
- }
-
- } /* BeginDrawing*/
-
-
-
- /*******************
- Back to "Square 1":
- *******************/
-
- /* OVERRIDE: */
- void CColorBitMap::EndDrawing (void)
- {
-
- if (macPort == NULL)
- {
- if (gSystem.hasColorQD)
- SetPortPix( savePixMap );
- else
- SetPortBits( &saveBitMap );
- }
- else
- {
- SetPort( savePort );
- }
-
- if (gSystem.hasColorQD) SetGDevice( saveDevice );
-
- } /* EndDrawing*/
-
-
-
- /* OVERRIDE: */
- void CColorBitMap::CopyFrom (LongRect *fromRect, LongRect *toRect,
- RgnHandle maskRgn)
- {
- RGBColor saveForeColor, saveBackColor,
- aBlackColor = {0x0000, 0x0000, 0x0000},
- aWhiteColor = {0xFFFF, 0xFFFF, 0xFFFF};
-
- if (gSystem.hasColorQD)
- {
- GetForeColor( &saveForeColor );
- GetBackColor( &saveBackColor );
- ;
- RGBForeColor( &aBlackColor );
- RGBBackColor( &aWhiteColor );
- }
-
- LCopyBits( &macPort->portBits,
- &thePort->portBits,
- fromRect, toRect,
- xferMode, maskRgn );
-
- if (gSystem.hasColorQD)
- {
- RGBForeColor( &saveForeColor );
- RGBBackColor( &saveBackColor );
- }
-
- } /* CopyFrom */
-
-
-
- /* OVERRIDE: */
- void CColorBitMap::CopyTo (LongRect *fromRect, LongRect *toRect,
- RgnHandle maskRgn)
- {
- RGBColor aBlackColor = {0x0000, 0x0000, 0x0000},
- aWhiteColor = {0xFFFF, 0xFFFF, 0xFFFF};
-
- /*
- Set the blasted Port to off-screen:
- ( Thanks to America Online )
- */
- BeginDrawing();
- ;
- if (gSystem.hasColorQD)
- {
- /*
- Since the destination is our off-screen pixmap,
- we do NOT need to restore the old foreground
- and background colors as we did within CopyFrom:
-
- GetForeColor( &saveForeColor );
- GetBackColor( &saveBackColor );
- */
- RGBForeColor( &aBlackColor );
- RGBBackColor( &aWhiteColor );
- }
-
- LCopyBits( &thePort->portBits,
- &macPort->portBits,
- fromRect, toRect,
- xferMode, maskRgn );
- ;
- EndDrawing(); /* Restore status quo. */
-
- } /* CopyTo */
-
-
-
- /* OVERRIDE: */
- void CColorBitMap::GetBounds (LongRect *theBounds)
- {
- /* Once again, just to avoid having to HLock "itsPixMap": */
-
- Rect itsBounds;
-
-
- if (gSystem.hasColorQD)
- itsBounds = (**itsPixMap).bounds;
- else
- itsBounds = macPort->portBits.bounds;
-
- QDToLongRect( &itsBounds, theBounds );
-
- } /* GetBounds */
-
-
-
- /* OVERRIDE: */
- void CColorBitMap::SetBoundsOrigin (short hOrigin, short vOrigin)
- {
- LongRect newLBounds;
- Rect newSBounds;
-
-
- if ( gSystem.hasColorQD && (!itsPixMap || !itsMaxDevice) )
- /* Don't even try!!! */
- return;
-
- GetBounds( &newLBounds );
-
- /* CBitMap::SetBoundsOrigin(...): */
- newLBounds.right += hOrigin - newLBounds.left;
- newLBounds.bottom += vOrigin - newLBounds.top;
- newLBounds.left = hOrigin;
- newLBounds.top = vOrigin;
- ;
- LongToQDRect( &newLBounds, &newSBounds );
-
- RectRgn( macPort->visRgn, &newSBounds );
- macPort->portRect = newSBounds;
- RectRgn( macPort->clipRgn, &newSBounds );
- if (gSystem.hasColorQD)
- {
- (**itsPixMap).bounds = newSBounds;
- /* (**itsMaxDevice).gdPMap = itsPixMap; */
- (**itsMaxDevice).gdRect = newSBounds;
- }
- else
- {
- macPort->portBits.bounds = newSBounds;
- }
-
- } /* SetBoundsOrigin */
-
-
-
- /* PRIVATE: */
- CTabHandle CColorBitMap::SetupPixMap (PixMapHandle aPixMap, Handle imageBits,
- short bytesPerRow, Rect *theBounds)
- {
- CTabHandle newColors; /* Color table used for */
- /* the off-screen PixMap. */
- short depth;
- Boolean savedAlloc;
- OSErr error;
-
-
- newColors = nil;
- depth = (**aPixMap).pixelSize;
-
- /* Clone the clut if indexed color; allocate a dummy clut if direct color: */
- savedAlloc = SetAllocation( kAllocCanFail );
- ;
- if (depth <= 8)
- {
- newColors = (**aPixMap).pmTable;
- error = HandToHand( (Handle*)&newColors );
- }
- else
- {
- newColors = (CTabHandle) NewHandleClear( sizeof(ColorTable) -
- sizeof(CSpecArray) );
- error = MemError();
- }
- ;
- SetAllocation( savedAlloc );
-
- if (error != noErr)
- {
- return (nil);
- }
-
- /* Initialize fields common to indexed and direct PixMaps: */
- (**aPixMap).baseAddr = *imageBits; /* Point to image. */
- (**aPixMap).rowBytes = bytesPerRow | 0x8000; /* MSB set for PixMap. */
- (**aPixMap).bounds = *theBounds;
- (**aPixMap).pmVersion = 0; /* No special stuff. */
- (**aPixMap).packType = 0; /* Default PICT pack. */
- (**aPixMap).packSize = 0; /* Always zero in memory. */
- (**aPixMap).hRes = kDefaultRes; /* 72 DPI default res. */
- (**aPixMap).vRes = kDefaultRes; /* 72 DPI default res. */
- /* (**aPixMap).pixelSize = depth; -- Already done. */
- (**aPixMap).planeBytes = 0; /* Not used. */
- (**aPixMap).pmReserved = 0; /* Not used. */
-
- /* Initialize fields specific to indexed and direct PixMaps: */
- if (depth <= 8)
- {
- (**aPixMap).pixelType = 0; /* Indicates indexed. */
- (**aPixMap).cmpCount = 1; /* Have 1 component. */
- (**aPixMap).cmpSize = depth; /* Component size = depth. */
- (**aPixMap).pmTable = newColors; /* Handle to CLUT. */
- }
- else
- {
- (**aPixMap).pixelType = RGBDirect; /* Indicates direct. */
- (**aPixMap).cmpCount = 3; /* Have 3 components. */
- if (depth == 16)
- (**aPixMap).cmpSize = 5; /* 5 bits/component. */
- else
- (**aPixMap).cmpSize = 8; /* 8 bits/component. */
- (**newColors).ctSeed = 3 * (**aPixMap).cmpSize;
- (**newColors).ctFlags = 0;
- (**newColors).ctSize = 0;
- (**aPixMap).pmTable = newColors;
- }
-
- return (newColors);
-
- } /* SetupPixMap*/
-
-
-
- /* PRIVATE: */
- GDHandle CColorBitMap::CreateGDevice (PixMapHandle basePixMap)
- {
- GDHandle newDevice; /* Handle to the new GDevice. */
- ITabHandle embryoITab; /* Handle to the embryonic inverse table. */
- Boolean savedAlloc;
- Rect deviceRect; /* Rectangle of GDevice. */
- short depth;
-
-
- /* Initialize a few things before we begin: */
- newDevice = nil;
- embryoITab = nil;
-
- /* Allocate memory for the new GDevice: */
- savedAlloc = SetAllocation( kAllocCanFail );
- newDevice = (GDHandle) NewHandleClear( sizeof(GDevice) );
- SetAllocation( savedAlloc );
- if (newDevice != nil)
- {
-
- /* Allocate the embryonic inverse table: */
- savedAlloc = SetAllocation( kAllocCanFail );
- embryoITab = (ITabHandle) NewHandleClear( 2L );
- SetAllocation( savedAlloc );
- if (embryoITab == nil)
- {
- DisposHandle( (Handle)newDevice );
- return (nil);
- }
-
- /* Set rectangle of device to PixMap bounds: */
- deviceRect = (**basePixMap).bounds;
-
- depth = (**basePixMap).pixelSize;
-
- /* Initialize the new GDevice fields: */
- (**newDevice).gdRefNum = 0; /* Only used for screens. */
- (**newDevice).gdID = 0; /* Won’t normally use. */
- if (depth <= 8)
- (**newDevice).gdType = clutType; /* Depth ≤ 8; clut device. */
- else
- (**newDevice).gdType = directType; /* Depth > 8; direct device. */
- (**newDevice).gdITable = embryoITab; /* 2-byte handle for now. */
- (**newDevice).gdResPref = kITabRes; /* Normal inv table res. */
- (**newDevice).gdSearchProc = nil; /* No color-search proc. */
- (**newDevice).gdCompProc = nil; /* No complement proc. */
- (**newDevice).gdFlags = 0; /* Will set these below. */
- (**newDevice).gdPMap = basePixMap; /* Reference our PixMap. */
- (**newDevice).gdRefCon = 0; /* Won’t normally use. */
- (**newDevice).gdNextGD = nil; /* Not in GDevice list. */
- (**newDevice).gdRect = deviceRect; /* Use PixMap dimensions. */
- (**newDevice).gdMode = -1; /* For non-screens. */
- (**newDevice).gdCCBytes = 0; /* Only used for screens. */
- (**newDevice).gdCCDepth = 0; /* Only used for screens. */
- (**newDevice).gdCCXData = 0; /* Only used for screens. */
- (**newDevice).gdCCXMask = 0; /* Only used for screens. */
- (**newDevice).gdReserved = 0; /* Currently unused. */
-
- /* Set color-device bit if PixMap isn’t black & white: */
- if (depth > 1)
- SetDeviceAttribute( newDevice, gdDevType, true );
-
- /* Set bit to indicate that the GDevice has no video driver: */
- SetDeviceAttribute( newDevice, noDriver, true );
-
- /* Initialize the inverse table: */
- if (depth <= 8)
- {
- MakeITable( (**basePixMap).pmTable, (**newDevice).gdITable,
- (**newDevice).gdResPref );
- if ( QDError() != noErr )
- {
- DisposHandle( (Handle)newDevice );
- DisposHandle( (Handle)embryoITab );
- newDevice = nil;
- }
- }
-
- } /* newDevice != nil */
-
- return (newDevice);
-
- } /* CreateGDevice*/
-
-
-
-
- /* end: "CColorBitMap.cp" */
-